<template>
    <Layout>
        <template #left-content>
            <!-- 用于拖动窗口的控件 -->
            <div class="drag-panel drag"></div>
            <div class="top-search">
                <!-- 搜索 -->
                <el-input clearable placeholder="搜索" v-model="searchKey" size="small" @keyup="search">
                    <template #suffix>
                        <span class="iconfont icon-sousuo">
                        </span>
                    </template>
                </el-input>
            </div>
            <div class="chat-session-user-list" v-show="!searchKey">
                <template v-for="(item, index) in chatSessionUserList" :key="index">
                    <ChatSession :data="item" @click="onChatSessionClick(item)"
                        @contextmenu.stop="onContextMenu(item, $event)"
                        :isCurrentSession="item.contactId == currentChatSession.contactId"></ChatSession>
                </template>
            </div>
            <div class="search-list" v-show="searchKey">
                <SearchResult :data="item" v-for="(item, index) in searchList" :key="index"
                    @click="searchClickHandler(item)">
                </SearchResult>
            </div>
        </template>
        <template #right-content>
            <div class="title-panel drag">
                <div class="title">
                    <span>{{ currentChatSession.contactName }}</span>
                    <!-- 如果是群聊要显示群聊人数 -->
                    <span v-if="currentChatSession.contactType == 1 || currentChatSession.contactType == 'GROUP'">
                        ({{ currentChatSession.memberCount }})
                    </span>
                </div>
            </div>
            <!-- 群聊更多操作 -->
            <div class="iconfont icon-gengduo no-drag"
                v-if="currentChatSession.contactType == 1 || currentChatSession.contactType == 'GROUP'"
                @click="showGroupDetail"></div>
            <div class="chat-panel" v-show="Object.keys(currentChatSession).length > 0">
                <div class="message-panel" id="message-panel">
                    <div class="message-item" v-for="(item, index) in chatMessageList" :key="index"
                        :id="'message' + item.messageId">
                        <!-- 中间显示的时间 -->
                        <!-- 距离前一个消息超过2分钟才显示时间 -->
                        <template
                            v-if="index == 0 || index > 1 && item.sendTime - chatMessageList[index - 1].sendTime >= 120000">
                            <ChatMessageTime :data="item"></ChatMessageTime>
                        </template>
                        <!-- 左右显示的文本消息 -->
                        <ChatMessage :data="item" :currentChatSession="currentChatSession"
                            @showDetail="showMediaDetailHandler"></ChatMessage>
                    </div>
                </div>
            </div>
            <div v-if="Object.keys(currentChatSession).length > 0">
                <MessageSend :currentChatSession="currentChatSession" @addMessage="onAddMessage"></MessageSend>
            </div>
            <div v-else>
                <Blank></Blank>
            </div>
        </template>
    </Layout>
    <ChatGroupDetail ref="chatGroupDetailRef" @delChatSession="delChatSession"></ChatGroupDetail>
</template>

<script>
export default {
    name: 'chat'
}
</script>

<script setup>
import { ref, reactive, getCurrentInstance, nextTick, watch, onMounted, onUnmounted } from "vue"
import { useRoute, useRouter } from "vue-router";
const { proxy } = getCurrentInstance();
const route = useRoute();
const router = useRouter();

import ContextMenu from '@imengyu/vue3-context-menu'
import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css'

import ChatSession from "./ChatSession.vue";
import MessageSend from "./MessageSend.vue";
import ChatMessage from "./ChatMessage.vue";
import ChatMessageTime from "./ChatMessageTime.vue";
import ChatGroupDetail from "./ChatGroupDetail.vue";

// 当前的会话信息
const currentChatSession = ref({});
// 当前的聊天信息列表
const chatMessageList = ref([]);
// 聊天分页信息
const chatMessagePageInfo = {
    pageTotal: 0,
    pageNo: 0,
    noData: false,  // 标记是否已经查完了所有数据
    // 获取第一页数据的时候记录的最后一条信息的id，之后的分页依据加上大于该id的条件
    // 确保如果有新消息到来，不会影响到原来的分页情况
    maxMessageId: null
};

import SearchResult from "./SearchResult.vue";

const searchKey = ref("");
const searchList = ref([]);
const search = () => {
    searchList.value = [];
    if (!searchKey.value) {
        return;
    }
    const regex = new RegExp("(" + searchKey.value + ")", "ig");  //  g：表示全局（global）模式，即模式将被应用于所有字符串，而非在发现第一个匹配项时立即停止；i：表示不区分大小写（case-insensitive）模式，即在确定匹配项时忽略模式与字符串的大小写
    chatSessionUserList.value.forEach(item => {
        if (item.contactName.includes(searchKey.value) || item.lastMessage.includes(searchKey.value)) {
            let newItem = Object.assign({}, item);
            newItem.searchContactName = item.contactName.replace(regex, "<span class='highlight'>" + searchKey.value + "</span>");
            newItem.searchLastMessage = item.lastMessage.replace(regex, "<span class='highlight'>" + searchKey.value + "</span>");
            searchList.value.push(newItem);
        }
    });
};

const searchClickHandler = (item) => {
    currentChatSession.value = item;
    searchKey.value = "";
    onChatSessionClick(item);
}

const chatGroupDetailRef = ref(null);
/* 点击更多按钮展示群聊详情 */
const showGroupDetail = () => {
    // console.log(currentChatSession.value);
    chatGroupDetailRef.value.show(currentChatSession.value.contactId);
}

/* 点击会话名片时获取对应的消息数据，展示到右侧 */
const onChatSessionClick = async (item) => {
    distanceToBottom.value = 0;  // 重新设置到达底部的距离为0
    // 清空未读聊天消息数量
    if (item.noReadCount) {
        messageCountStore.setCount('chatCount', -item.noReadCount, false)
    }
    // 如果传进来的item是{}的话是想要清空会话的，然后在模板里用Object.keys(currentChatSession).length > 0选择是否展示右侧的消息和发送消息的组件
    // 所以如果传入了{}，不能补充这个noReadCount属性，否则长度就不是0了，会导致右侧还是显示了输入框
    if (Object.keys(item).length > 0) {
        item.noReadCount = 0;
    }
    // 设置为当前的会话
    currentChatSession.value = Object.assign({}, item);
    // 获取聊天信息列表
    // 先清空原来的数据
    chatMessageList.value = [];
    // 清空原来的分页信息
    chatMessagePageInfo.pageNo = 0;
    chatMessagePageInfo.noData = false;
    chatMessagePageInfo.maxMessageId = null;
    // 获取消息列表
    await loadChatMessageList();
    // 保存当前的sessionId到store，主进程需要获取（比如说来消息的时候如果不是选中的session则那个session未读消息数要+1）
    // 同时根据contactId清空本地数据库中对应的未读消息数量
    myApi.setSessionInfo({
        sessionId: item.sessionId,
        contactId: item.contactId
    });
    // console.log('chatCount: ', messageCountStore.getCount('chatCount'));
};

/* 滚动条到达底部的距离，是来了新消息后是否要滚动到底部的指标，防止在滚动查看消息时因为对面发送了消息导致一直查看不了历史消息 */
const distanceToBottom = ref(0);

/* 滚动到底部 */
const tryScrollToBottom = () => {
    if (distanceToBottom.value > 400) return; // 如果距离底部已经大于400，则不需要滚动
    nextTick(() => {
        // 找到所有消息项
        const items = document.querySelectorAll(".message-item");
        if (items.length > 0) {
            setTimeout(() => {
                // 滚动到最后一个消息的位置
                items[items.length - 1].scrollIntoView()
            }, 50)
        }
    })
}

const onAddMessage = (data) => {
    // 将消息加入到列表中
    chatMessageList.value.push(data)
    // 更新左侧消息对应的会话的显示内容
    // 先找到对应的会话
    const session = chatSessionUserList.value.find(item => {
        return item.sessionId == data.sessionId;
    })
    if (session) {
        // 更新会话
        session.lastMessage = data.lastMessage;
        session.lastReceiveTime = data.sendTime;
    }
    // 发送了消息后对会话重新排序，即把聊天的这个会话往上提
    sortChatSessionList(chatSessionUserList.value);
    // 滚动到底部
    tryScrollToBottom();
}

const loadChatMessageList = async () => {
    if (chatMessagePageInfo.noData) return; // 数据已经全部加载完
    // 页数++
    chatMessagePageInfo.pageNo++;
    const { pageTotal, dataList } = await myApi.getChatMessageList({
        sessionId: currentChatSession.value.sessionId,
        pageNo: chatMessagePageInfo.pageNo,
        maxMessageId: chatMessagePageInfo.maxMessageId
    })
    // 更新分页信息
    chatMessagePageInfo.pageTotal = pageTotal;
    if (chatMessagePageInfo.pageNo == pageTotal) {
        chatMessagePageInfo.noData = true;  // 标记已经没有数据了
    }
    // 对消息进行排序
    dataList.sort((a, b) => {
        return a.messageId - b.messageId;
    })
    const lastMessage = chatMessageList.value[0]
    // 将数据添加到列表中
    chatMessageList.value = dataList.concat(chatMessageList.value);
    // 如果是第一页查询，需要标记最后的那条消息的id，作为以后分页的依据，确保分页不受新消息影响
    if (chatMessagePageInfo.pageNo == 1) {
        chatMessagePageInfo.maxMessageId = dataList.length > 0 ? dataList[dataList.length - 1].messageId : null;
        // 滚动到底部
        tryScrollToBottom();
    } else {
        // 不是第一页，即向上滚动获取消息，滚动条的位置需要在原本最上面的信息的地方
        nextTick(() => {
            document.querySelector("#message" + lastMessage.messageId).scrollIntoView()
        })
    }
    // console.log(chatMessageList.value)
}

/* 排序，置顶聊天在前，有新消息的在前 */
const sortChatSessionList = (list) => {
    list.sort((a, b) => {
        const topTypeResult = b.topType - a.topType;
        if (topTypeResult == 0) {
            return b.lastReceiveTime - a.lastReceiveTime;
        }
        return topTypeResult;
    })
}

const setChatSessionTop = (data) => {
    data.topType = data.topType == 0 ? 1 : 0;
    sortChatSessionList(chatSessionUserList.value)  // 排序
    myApi.setChatSessionTop({ contactId: data.contactId, topType: data.topType });
}

const delChatSession = async (data) => {
    await myApi.delChatSession(data.contactId);
    // 将该会话从列表中移除
    setTimeout(() => {
        chatSessionUserList.value = chatSessionUserList.value.filter(item => item.contactId != data.contactId)
    }, 100)
    // 清空当前会话
    await onChatSessionClick({})
    // console.log("currentChatSession: ", currentChatSession.value)
}

const onContextMenu = (data, event) => {
    ContextMenu.showContextMenu({
        // 显示在鼠标位置
        x: event.x,
        y: event.y,
        items: [
            {
                label: data.topType == 0 ? "置顶" : "取消置顶",
                onClick: () => { setChatSessionTop(data) }
            },
            {
                label: "删除聊天",
                onClick: () => {
                    proxy.Confirm({
                        message: `确定要删除与【${data.contactName}】的聊天吗？`,
                        okFun: () => { delChatSession(data) }
                    })
                }
            }
        ]
    })
}

/* 会话列表 */
const chatSessionUserList = ref([]);
/* 获取会话列表 */
const loadChatSessionUserList = async () => {
    chatSessionUserList.value = await myApi.getChatSessionUserList()
    sortChatSessionList(chatSessionUserList.value)  // 排序
    // 计算未读聊天信息数量
    let noReadCount = 0;
    chatSessionUserList.value.forEach(item => {
        noReadCount += item.noReadCount
    })
    messageCountStore.setCount('chatCount', noReadCount, true)
}

import { useMessageCountStore } from "@/stores/MessageCountStore";
const messageCountStore = useMessageCountStore();

myApi.onReceiveMessage(async (event, data) => {
    // console.log("receive message: ", data, " currentChatSession: ", currentChatSession.value)
    switch (data.noticeType) {
        case "CONTACT_APPLY":  // 好友申请
            const setting = await myApi.getUserSetting()
            const noReadCount = setting.contactNoRead
            // 更新渲染进程状态信息
            messageCountStore.setCount('contactApplyCount', noReadCount, true)
            // console.log(noReadCount);
            break;
        case "CONTACT_RENAME":  // 好友或群聊修改名字
            // 找到对应的会话
            let ss = chatSessionUserList.value.find(item => {
                return item.contactId == data.contactId;
            })
            if (ss == null) break;
            ss.contactName = data.contactName;  // 更新会话的名字
            break;
        /* 下面这类都是展示到聊天界面的内容 */
        case "CHAT":  // 聊天消息
        case "USER_ADD_ACCEPT":  // 自己同意别人的好友申请
        case "USER_ADD_BY_OTHERS":  // 别人同意添加自己为好友
        case "GROUP_CREATED":  // 群聊创建成功
        case "GROUP_DISBAND":  // 群聊解散
        case "GROUP_ADD_ACCEPTED":  // 加群申请被接受
        case "GROUP_MEMBER_LEAVE_OR_IS_REMOVED":  // 群成员退群或被踢出
            // 找到对应的会话
            const session = chatSessionUserList.value.find(item => {
                return item.sessionId == data.sessionId;
            })
            if (session == null) {
                // 可能把这个会话删了，要重新加入这个会话
                chatSessionUserList.value.push(data);
            } else {
                // 给会话重新赋值
                data.memberCount = data.memberCount ? data.memberCount : session.memberCount; // 有时候没有把memberCount传过来，就用原来的，防止覆盖
                Object.assign(session, data);
            }
            // 重新排序会话列表
            sortChatSessionList(chatSessionUserList.value);
            if (Object.keys(currentChatSession).length == 0 || data.sessionId != currentChatSession.value.sessionId) {
                // 当前没有选中任何会话或者来的消息不是当前聊天对象发的，需要增加总的未读消息数量
                // console.log(currentChatSession.value?.sessionId)
                messageCountStore.setCount('chatCount', 1, false);
                // console.log(messageCountStore.getCount('chatCount'))
            } else {
                // 是当前会话发送的消息，为当前会话重新赋值
                Object.assign(currentChatSession.value, data);
                // 添加这个聊天信息
                chatMessageList.value.push(data);
                // 滚动到底部
                tryScrollToBottom();
            }
            break;
        case "FILE_UPLOAD_COMPLETED":
            // 找到对应的消息，把消息状态改为已发送（对方发送的文件消息）
            const message = chatMessageList.value.find(item => {
                if (item.messageId == data.messageId) {
                    return item;
                }
            })
            if (message) {
                message.sendStatus = 1; // 已发送
            }
            // 对方文件上传完毕，把原来的消息更新即可，不需要再添加到列表中
            break;
        case "FORCE_OFFLINE":  // 强制下线
            proxy.Confirm({
                message: "您已被管理员强制下线，请重新登录！",
                okFun: () => {
                    setTimeout(() => {
                        myApi.reLogin()
                    }, 200)
                },
                showCancelBtn: false
            });
            break;
    }
})

/* 文件上传完毕后回调通知渲染进程展示文件的缩略图 */
myApi.onFileUploadDone((event, data) => {
    // 将文件对应的消息设置为已发送即可（发送中显示的是那个动态图），消息就会渲染文件的缩略图
    const message = chatMessageList.value.find(item => {
        if (item.messageId == data.messageId) {
            return item;
        }
    });
    if (message) {
        message.sendStatus = 1; // 已发送
    }
})

/* 展示媒体消息详情 */
const showMediaDetailHandler = (messageId) => {
    // 找出所有媒体文件，因为可以左右选择查看上一个/下一个图片/视频
    let mediaFileList = chatMessageList.value.filter(item => {
        return item.fileType != null;
    });
    mediaFileList = mediaFileList.map(item => {
        return {
            partType: "chat",
            fileId: item.messageId,
            fileType: item.fileType,
            fileName: item.fileName,
            fileSize: item.fileSize,
            forceGet: false
        }
    })
    // console.log("mediaFileList", mediaFileList);
    // 通知主进程打开新窗口展示信息
    myApi.openMediaDetailWindow({
        windowId: "media-detail",
        title: "媒体文件",
        path: "/showMediaDetail",
        data: {
            mediaFileList: mediaFileList,
            currentFileId: messageId
        }
    });
}

/* 跳转到给contactId对应的联系人的聊天窗口 */
const sendMessage = async (contactId) => {
    // 找到对应的会话
    let session = chatSessionUserList.value.find(item => {
        return item.contactId == contactId;
    })
    if (!session) {
        // 可能是之前删除过这个会话，需要重新加入这个会话
        // myApi.getChatSessionUserList() 只获取了显示的会话，没有被删除的会话，所以要用新的自定义api
        session = await myApi.reloadChatSession(contactId)
        chatSessionUserList.value.push(session);
        sortChatSessionList(chatSessionUserList.value);  // 重新排序会话列表
    }
    // 等价于点击会话
    await onChatSessionClick(session)
}

/* 监听路由变化，可能点击了名片的发消息按钮 */
watch(
    () => route.path,
    async (newVal, oldVal) => {
        if (route.query.timestamp && route.query.chatId) {
            await sendMessage(route.query.chatId);
        }
        if (route.path.indexOf('/chat') == -1) {
            // 不在聊天界面，清空当前会话
            await onChatSessionClick({});
        }
        // console.log(route, currentChatSession.value)
    }
)

onMounted(async () => {
    // 进入聊天页面时，获取会话列表
    await loadChatSessionUserList()
    // 滚动条获取历史消息的事件监听
    nextTick(() => {
        const messagePanel = document.querySelector("#message-panel");
        messagePanel.addEventListener("scroll", async (e) => {
            const scrollTop = e.target.scrollTop;
            // 更新距离底部的距离
            distanceToBottom.value = messagePanel.scrollHeight - scrollTop - messagePanel.clientHeight;
            // console.log("distanceToBottom", distanceToBottom.value)
            // chatMessageList.value.length > 0 不然刚点击会话时还没有加载消息，显示就是在最顶部，就会触发一次，加载数据后如果消息不够多还是在顶部，导致触发两次
            if (scrollTop == 0 && chatMessageList.value.length > 0) {
                // 滚动条滚到顶部，获取历史消息
                await loadChatMessageList();
            }
        })
    })
    // 第一次进入聊天界面默认不选择会话
    myApi.setSessionInfo({
        sessionId: '',
        contactId: ''
    });
})
onUnmounted(() => {
    myApi.removeAllListeners("receiveMessage")
    myApi.removeAllListeners("fileUploadDone")
})
</script>

<style lang="scss" scoped>
.drag-panel {
    height: 25px;
    background: #f7f7f7;
}

.top-search {
    padding: 0 10px 9px 10px;
    background: #f7f7f7;
    display: flex;
    align-items: center;

    .iconfont {
        font-size: 12px;
    }
}

.chat-session-user-list {
    height: calc(100vh - 62px);
    overflow: hidden;
    border-top: 1px solid #ddd;

    &:hover {
        overflow: auto;
    }
}

.search-list {
    height: calc(100vh - 62px);
    background: #f7f7f7;
    overflow: hidden;

    &:hover {
        overflow: auto;
    }
}

.title-panel {
    display: flex;
    align-items: center;

    .title {
        height: 60px;
        line-height: 60px;
        padding-left: 10px;
        font-size: 18px;
        color: #000000;
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.icon-gengduo {
    position: absolute;
    z-index: 1;
    top: 30px;
    right: 3px;
    width: 20px;
    font-size: 20px;
    margin-right: 5px;
    cursor: pointer;
}

.chat-panel {
    border-top: 1px solid #ddd;
    background: #f5f5f5;

    .message-panel {
        padding: 10px 30px 0 30px;
        height: calc(100vh - 200px - 62px);
        overflow: auto;

        .message-item {
            margin-bottom: 15px;
            text-align: center;
        }
    }
}
</style>
